/**
 * 
 */
package com.williamoneil.charts.server.controller;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javax.servlet.ServletContext;

import com.williamoneil.charts.server.ApplicationException;
import com.williamoneil.charts.shared.SymbolData;

/**
 * @author Gudipati
 *
 */
public class SymbolSearchController {

	private static final Logger logger  = Logger.getLogger(SymbolSearchController.class.getName());
	private static Map<String, SymbolData> _symbolMap = null; 
	private static ServletContext _context = null;
	
	private SymbolSearchController(final ServletContext context) throws ApplicationException {
		_context = context;
		// Check whether the company-list-files are upto date
		checkCompanyListFiles();
	}
	
	private static SymbolSearchController _instance = null;
	public static SymbolSearchController getInstance(final ServletContext context) throws ApplicationException {
		if(_instance == null) {
			_instance = new SymbolSearchController(context);
		}
		
		return _instance;
	}
	
	
	public SymbolData getSymbolData(final String symbol) {
		if(symbol == null) {
			return null;
		}
		
		final String symbolUpper = symbol.trim().toUpperCase();
		return _symbolMap.get(symbolUpper);		
	}
	
	
	// keys
	private static final String LOAD_TIME_KEY = "load.time";
	
	private static final DateFormat _sdf = new SimpleDateFormat("MM.dd.yyyy");
	
	private static final String NASDAQ_TEXT = "nasdaq";
	private static final String NYSE_TEXT = "nyse";
	private static final String AMEX_TEXT = "amex";
	
	public static synchronized void checkCompanyListFiles() throws ApplicationException {
		try {
			final Properties dataProps = new Properties();
			final InputStream ris = _context.getResourceAsStream("WEB-INF/data/data.props");
			if(ris != null) {
				dataProps.load(ris);
			}
			
			final String loadTimeStr = dataProps.getProperty(LOAD_TIME_KEY);
			Date loadTime = null;
			if(loadTimeStr != null) {
				try {
					loadTime = _sdf.parse(loadTimeStr);
					
				} catch(Exception ex) {
					logger.warning("error parsing the load.time. This will result in loading the data.");
					loadTime = null;
				}
			}
			
			if(loadTime == null) {
				loadTime = new Date(0);
				final Calendar loadTimeCal = Calendar.getInstance();
				loadTimeCal.setTime(loadTime);

				final Calendar nowCal = Calendar.getInstance();
				// we get the files if the difference is more than a day
				// 24 hours = 1 day
				if(nowCal.get(Calendar.YEAR) != loadTimeCal.get(Calendar.YEAR) || 
						nowCal.get(Calendar.DAY_OF_YEAR) > loadTimeCal.get(Calendar.DAY_OF_YEAR)) {
					// load the files
					final Map<String, SymbolData> symbolMap = new HashMap<String, SymbolData>(); 
					downloadCompanyListFileForExchange(NASDAQ_TEXT, symbolMap);
					downloadCompanyListFileForExchange(NYSE_TEXT, symbolMap);
					downloadCompanyListFileForExchange(AMEX_TEXT, symbolMap);
					
					_symbolMap = symbolMap;
				}
			}
			
			// load from local store if we cannot download it
			if(_symbolMap == null || _symbolMap.size() == 0) {
				_symbolMap = new HashMap<String, SymbolData>();
				
				loadCompanyListFileForExchange(NASDAQ_TEXT, _symbolMap);
				loadCompanyListFileForExchange(NYSE_TEXT, _symbolMap);
				loadCompanyListFileForExchange(AMEX_TEXT, _symbolMap);
			}
		} catch(Exception ex) {
			throw new ApplicationException(ex);
		}
 	}
	
	private static final String COMPANY_LIST_URL_TEMPLATE = "http://www.nasdaq.com/screening/companies-by-name.aspx?letter=0&exchange=%s&render=download";
	private static final void downloadCompanyListFileForExchange(final String exchangeName, Map<String, SymbolData> symbolMap) throws Exception {
		if(exchangeName == null || symbolMap == null) {
			return;
		}
		
		final URL url = new URL(String.format(COMPANY_LIST_URL_TEMPLATE, exchangeName));
		final InputStream is = url.openStream();
		
		loadCompanyListFileForExchange(exchangeName, symbolMap, is);
	}
	
	private static final String COMPANY_LIST_FILE_TEMPLATE = "data/companylist_%s.csv";
	private static final void loadCompanyListFileForExchange(final String exchangeName, Map<String, SymbolData> symbolMap) throws Exception {
		if(exchangeName == null || symbolMap == null) {
			return;
		}
		
		final String fileName = String.format(COMPANY_LIST_FILE_TEMPLATE, exchangeName); 
		final InputStream is = SymbolSearchController.class.getResourceAsStream(fileName); 
		
		loadCompanyListFileForExchange(exchangeName, symbolMap, is);
	}
	
	private static final void loadCompanyListFileForExchange(final String exchangeName, Map<String, SymbolData> symbolMap, final InputStream is) throws Exception {
		if(exchangeName == null || is == null || symbolMap == null) {
			return;
		}
		
		try {			
			final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
			String readLine = reader.readLine();
			// process until end-of file
			while(true) {
				readLine = reader.readLine();
				if(readLine == null) {
					break;
				}
				
				final StringTokenizer tokenz = new StringTokenizer(readLine, ",", false);
				int index = 0;
				
				final SymbolData aSymbol = new SymbolData();
				aSymbol.setExchange(exchangeName);
				
				while(tokenz.hasMoreElements()) {
					index++;
					String aToken = tokenz.nextToken();
					if(aToken == null) {
						continue;
					}
					
					// replace all occurances of ""
					if(aToken.startsWith("\"")) {
						aToken = aToken.substring(1, aToken.length() -1);
					}
					
					if(aToken.equalsIgnoreCase("n/a")) {
						aToken = "";
					}
					
					if(index == 1) {
						aSymbol.setSymbol(aToken.toUpperCase());
					} else if (index == 2) {
						aSymbol.setName(aToken);
					} 
					/*else if (index == 7) {
						aSymbol.setSector(aToken);
					} else if (index == 8) {
						aSymbol.setIndustry(aToken);
					}
					*/
				}
				
				if(aSymbol.getSymbol() != null) {
					symbolMap.put(aSymbol.getSymbol(), aSymbol);
				}
			}
			
		} finally {
			if(is != null) {
				is.close();
			}
		}
		
	}
}
